/**
 * Copyright (c) 2009  Collaborative Development Group, C.S. Dept., University of Bari
 *
 * All rights reserved. This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License v1.0  which accompanies this distribution, and is 
 * available at http://www.eclipse.org/legal/epl-v10.html
 */
package it.uniba.di.cdg.penelope.ui.mvp;

import static com.google.common.base.Preconditions.checkState;
import static com.google.inject.matcher.Matchers.annotatedWith;
import static com.google.inject.matcher.Matchers.subclassesOf;
import it.uniba.di.cdg.penelope.ui.mvp.databinding.IDataBindingService;
import it.uniba.di.cdg.penelope.ui.mvp.scope.ViewScoped;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import com.google.inject.Binder;
import com.google.inject.Key;

/**
 * Behind the scenes, it creates an interceptor that, after each method annotated with {@link PostConstruct} annotation
 * <ul>
 * <li>will execute the {@link IDataBindingService#bind(Object, Object)} service method, actually wiring model and 
 * view (default implementation uses the Eclipse DataBinding API).</li>
 * <li>will wire the presenter to the model</li>
 * </ul>
 */
public class MVPBuilder {
	private final Binder binder;
	private final String viewId;
	private Class<?> presentationModeltype;
	private Class<?> presenterType;
	private Class<?> viewType;
	private Class<?> modelType;

	/**
	 * @param binder 
	 * @param viewId
	 */
	public MVPBuilder( Binder binder, String viewId ) {
		super();
		this.binder = binder;
		this.viewId = viewId;
	}

	public MVPBuilder presentationModel( Class<?> modelType ) {
		this.presentationModeltype = modelType;
		return this;
	}

	public MVPBuilder view( Class<?> viewType ) {
		this.viewType = viewType;
		return this;
	}

	public MVPBuilder presenter( Class<?> presenterType ) {
		this.presenterType = presenterType;
		return this;
	}

	public MVPBuilder model( Class<?> modelType ) {
		this.modelType = modelType;
		return this;
	}
	
	/**
	 * Builds the configuration for the actual MVP triplet.
	 * FIXME We are losing the type-safety here!
	 */
	public void build() {
		ensureConfiguration();
		
		// Views are singleton, the others live up the view
//		binder.bind( viewType ).in( Singleton.class ); // XXX Views as singleton do not work here :S

		binder.bind( presentationModeltype ).in( ViewScoped.class );
		binder.bind( modelType ).in( ViewScoped.class );
		binder.bind( presenterType ).in( ViewScoped.class );
		
		// Apply databinding between model and view
		MVPWiringPostProcessor wire = new MVPWiringPostProcessor(
				viewId,
				binder.getProvider( Key.get( presentationModeltype ) ),
				binder.getProvider( Key.get( presenterType ) ) );
		binder.bindInterceptor( 
				subclassesOf( viewType ), 
				annotatedWith( PostConstruct.class ).or( annotatedWith( PreDestroy.class )), 
				wire );
		binder.requestInjection( wire );
	}

	/**
	 * Check for build  
	 */
	private void ensureConfiguration() {
		checkState( viewType != null, "No view provided in MVP configuration." );
		checkState( presenterType != null, "No presenter provided in MVP configuration." );
		checkState( presentationModeltype != null, "No presentation model provided in MVP configuration." );
		checkState( modelType != null, "No modelType provided in MVP configuration." );
	}
}